import sys
import time
from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, 
                              QHBoxLayout, QPushButton, QLabel, QLineEdit, 
                              QTextEdit, QGroupBox, QMessageBox, QComboBox)
from PySide6.QtCore import Qt, QThread, Signal, Slot
from PySide6.QtGui import QFont, QIcon, QDoubleValidator, QColor
import serial
import serial.tools.list_ports
import binascii
import crcmod

# 串口通信线程
class SerialThread(QThread):
    received_data = Signal(str)
    connection_status = Signal(bool)
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self.serial_port = None
        self.is_running = False
        
    def connect_serial(self, port_name, baud_rate):
        try:
            self.serial_port = serial.Serial(port_name, baud_rate, timeout=1)
            self.is_running = True
            self.connection_status.emit(True)
            self.received_data.emit(f"成功连接到串口 {port_name}, 波特率: {baud_rate}")
            return True
        except Exception as e:
            self.received_data.emit(f"串口连接失败: {str(e)}")
            self.connection_status.emit(False)
            return False
    
    def disconnect_serial(self):
        if self.serial_port and self.serial_port.is_open:
            self.is_running = False
            self.serial_port.close()
            self.connection_status.emit(False)
            self.received_data.emit("串口已断开连接")
    
    def run(self):
        while self.is_running:
            if self.serial_port and self.serial_port.is_open:
                try:
                    if self.serial_port.in_waiting:
                        data = self.serial_port.read(self.serial_port.in_waiting)
                        hex_data = binascii.hexlify(data).decode('utf-8').upper()
                        formatted_data = ' '.join([hex_data[i:i+2] for i in range(0, len(hex_data), 2)])
                        # self.received_data.emit(f"接收: {formatted_data}")
                        # 尝试将数据解析为字符串
                        try:
                            str_data = data.decode('utf-8', errors='replace')
                            # 过滤掉不可打印字符
                            printable_str = ''.join(c if c.isprintable() else '.' for c in str_data)
                            self.received_data.emit(f"接收: {formatted_data} | 字符串: {printable_str}")
                        except:
                            self.received_data.emit(f"接收: {formatted_data}")
                except Exception as e:
                    self.received_data.emit(f"读取数据错误: {str(e)}")
                    self.is_running = False
                    self.connection_status.emit(False)
            time.sleep(0.1)
    
    def send_data(self, data):
        if self.serial_port and self.serial_port.is_open:
            try:
                self.serial_port.write(data)
                hex_data = binascii.hexlify(data).decode('utf-8').upper()
                formatted_data = ' '.join([hex_data[i:i+2] for i in range(0, len(hex_data), 2)])
                self.received_data.emit(f"发送: {formatted_data}")
                return True
            except Exception as e:
                self.received_data.emit(f"发送数据错误: {str(e)}")
                return False
        else:
            self.received_data.emit("串口未连接，无法发送数据")
            return False

# 主窗口
class LoraControlApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("LORA串口调节工具")
        self.setMinimumSize(800, 600)
        self.setStyleSheet("""
            QMainWindow {
                background-color: #f5f5f5;
            }
            QGroupBox {
                border: 2px solid #3498db;
                border-radius: 5px;
                margin-top: 1ex;
                font-weight: bold;
                background-color: #ffffff;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                subcontrol-position: top center;
                padding: 0 5px;
                color: #2980b9;
            }
            QPushButton {
                background-color: #3498db;
                color: white;
                border-radius: 4px;
                padding: 6px 12px;
                font-weight: bold;
            }
            QPushButton:hover {
                background-color: #2980b9;
            }
            QPushButton:disabled {
                background-color: #cccccc;
                color: #666666;
            }
            QLineEdit {
                border: 1px solid #bdc3c7;
                border-radius: 4px;
                padding: 5px;
            }
            QTextEdit {
                border: 1px solid #bdc3c7;
                border-radius: 4px;
            }
            QComboBox {
                border: 1px solid #bdc3c7;
                border-radius: 4px;
                padding: 5px;
            }
        """)
        
        # 创建串口线程
        self.serial_thread = SerialThread()
        self.serial_thread.received_data.connect(self.update_log)
        self.serial_thread.connection_status.connect(self.update_connection_status)
        
        # 创建中央部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        # 主布局
        main_layout = QVBoxLayout(central_widget)
        
        # 串口设置区域
        serial_group = QGroupBox("串口设置")
        serial_layout = QHBoxLayout()
        
        # 串口选择
        self.port_combo = QComboBox()
        self.refresh_ports()
        serial_layout.addWidget(QLabel("串口:"))
        serial_layout.addWidget(self.port_combo)
        
        # 波特率选择
        self.baud_combo = QComboBox()
        for baud in ["9600", "19200", "38400", "57600", "115200"]:
            self.baud_combo.addItem(baud)
        self.baud_combo.setCurrentText("115200")
        serial_layout.addWidget(QLabel("波特率:"))
        serial_layout.addWidget(self.baud_combo)
        
        # 刷新和连接按钮
        self.refresh_btn = QPushButton("刷新")
        self.refresh_btn.clicked.connect(self.refresh_ports)
        serial_layout.addWidget(self.refresh_btn)
        
        self.connect_btn = QPushButton("连接")
        self.connect_btn.clicked.connect(self.toggle_connection)
        serial_layout.addWidget(self.connect_btn)
        
        serial_group.setLayout(serial_layout)
        main_layout.addWidget(serial_group)
        
        # 功能区域
        function_layout = QHBoxLayout()
        
        # 频点调节区域
        # 射频参数调节区域
        rf_group = QGroupBox("射频参数调节")
        rf_layout = QVBoxLayout()
        # freq_group = QGroupBox("射频参数调节")
        # freq_layout = QVBoxLayout()
        
        # 频点调节
        freq_input_layout = QHBoxLayout()
        freq_input_layout.addWidget(QLabel("频率 (Hz):"))
        
        self.freq_input = QLineEdit()
        self.freq_input.setPlaceholderText("410000000 - 960000000")
        # 设置验证器，只允许输入数字
        validator = QDoubleValidator(410000000, 960000000, 0)
        validator.setNotation(QDoubleValidator.StandardNotation)
        self.freq_input.setValidator(validator)
        freq_input_layout.addWidget(self.freq_input)
        
        self.set_freq_btn = QPushButton("设置频点")
        self.set_freq_btn.clicked.connect(self.set_frequency)
        self.set_freq_btn.setEnabled(False)
        freq_input_layout.addWidget(self.set_freq_btn)

        rf_layout.addLayout(freq_input_layout)

        # 发射功率调节
        power_input_layout = QHBoxLayout()
        power_input_layout.addWidget(QLabel("发射功率 (dBm):"))
        
        self.power_input = QLineEdit()
        self.power_input.setPlaceholderText("-3 - 22")
        # 设置验证器，只允许输入-3到22的数字
        power_validator = QDoubleValidator(-3, 22, 0)
        power_validator.setNotation(QDoubleValidator.StandardNotation)
        self.power_input.setValidator(power_validator)
        power_input_layout.addWidget(self.power_input)
        
        self.set_power_btn = QPushButton("设置功率")
        self.set_power_btn.clicked.connect(self.set_power)
        self.set_power_btn.setEnabled(False)
        power_input_layout.addWidget(self.set_power_btn)
        
        rf_layout.addLayout(power_input_layout)
        rf_group.setLayout(rf_layout)
        function_layout.addWidget(rf_group)
        # freq_layout.addLayout(freq_input_layout)
        # freq_group.setLayout(freq_layout)
        # function_layout.addWidget(freq_group)
        
        # 载波测试区域
        carrier_group = QGroupBox("载波测试")
        carrier_layout = QVBoxLayout()
        
        self.carrier_btn = QPushButton("开启载波测试")
        self.carrier_btn.clicked.connect(self.toggle_carrier)
        self.carrier_btn.setEnabled(False)
        self.carrier_status = False
        
        carrier_layout.addWidget(self.carrier_btn)
        carrier_group.setLayout(carrier_layout)
        function_layout.addWidget(carrier_group)
        
        main_layout.addLayout(function_layout)
        
        # 日志区域
        log_group = QGroupBox("日志")
        log_layout = QVBoxLayout()
        
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        log_layout.addWidget(self.log_text)
        
        clear_log_btn = QPushButton("清除日志")
        clear_log_btn.clicked.connect(self.clear_log)
        log_layout.addWidget(clear_log_btn)
        
        log_group.setLayout(log_layout)
        main_layout.addWidget(log_group)
        
        # 初始化日志
        self.update_log("欢迎使用LORA串口调节工具")
        self.update_log("请连接串口后使用功能")
        
        # 初始化CRC计算
        self.crc_func = crcmod.mkCrcFun(0x107, initCrc=0, rev=False, xorOut=0)
    
    def refresh_ports(self):
        self.port_combo.clear()
        ports = serial.tools.list_ports.comports()
        for port in ports:
            self.port_combo.addItem(port.device)
        if self.port_combo.count() == 0:
            self.update_log("未检测到串口设备")
    
    def toggle_connection(self):
        if self.connect_btn.text() == "连接":
            if self.port_combo.currentText():
                port = self.port_combo.currentText()
                baud = int(self.baud_combo.currentText())
                if self.serial_thread.connect_serial(port, baud):
                    self.serial_thread.start()
                    self.connect_btn.setText("断开")
                    self.set_freq_btn.setEnabled(True)
                    self.carrier_btn.setEnabled(True)
            else:
                self.update_log("请选择串口")
                QMessageBox.warning(self, "连接错误", "请先选择一个串口")
        else:
            self.serial_thread.disconnect_serial()
            self.connect_btn.setText("连接")
            self.set_freq_btn.setEnabled(False)
            self.carrier_btn.setEnabled(False)
            self.carrier_status = False
            self.carrier_btn.setText("开启载波测试")
    
    def update_connection_status(self, connected):
        if connected:
            self.connect_btn.setText("断开")
            self.set_freq_btn.setEnabled(True)
            self.carrier_btn.setEnabled(True)
        else:
            self.connect_btn.setText("连接")
            self.set_freq_btn.setEnabled(False)
            self.carrier_btn.setEnabled(False)
            self.carrier_status = False
            self.carrier_btn.setText("开启载波测试")
    
    def set_power(self):
        power_text = self.power_input.text()
        if not power_text:
            self.update_log("请输入发射功率")
            QMessageBox.warning(self, "输入错误", "请输入发射功率值")
            return
        
        try:
            power = int(power_text)
            if power < -3 or power > 22:
                error_msg = f"功率超出范围 (-3dBm - 22dBm)，当前值: {power}dBm"
                self.update_log(error_msg)
                QMessageBox.warning(self, "范围错误", error_msg)
                return
            
            # 如果载波测试已开启，先关闭载波测试
            if self.carrier_status:
                self.toggle_carrier()  # 这会关闭载波测试
            
            # 构建命令包
            # 命令格式: AA55 + 长度 + 命令类型(03表示设置功率) + 功率值(1字节) + 校验
            # 将功率值映射到0-25的范围（-3对应0，22对应25）
            power_byte = power + 3
            data = bytearray([0xAA, 0x55, 0x03, 0x03, power_byte])
            crc = self.crc_func(data)
            data.append(crc)
            
            if self.serial_thread.send_data(data):
                self.update_log(f"设置发射功率: {power} dBm")
        except ValueError:
            error_msg = f"功率格式错误: {power_text}"
            self.update_log(error_msg)
            QMessageBox.critical(self, "格式错误", error_msg)

    def set_frequency(self):
        freq_text = self.freq_input.text()
        if not freq_text:
            self.update_log("请输入频率")
            QMessageBox.warning(self, "输入错误", "请输入频率值")
            return
        
        try:
            freq = int(freq_text)
            if freq < 410000000 or freq > 960000000:
                error_msg = f"频率超出范围 (410MHz - 960MHz)，当前值: {freq}"
                self.update_log(error_msg)
                QMessageBox.warning(self, "范围错误", error_msg)
                return

            # 如果载波测试已开启，先关闭载波测试
            if self.carrier_status:
                self.toggle_carrier()  # 这会关闭载波测试
            
            # 构建命令包
            # 命令格式: AA55 + 长度 + 命令类型(01表示设置频率) + 频率值(4字节) + 校验
            freq_bytes = freq.to_bytes(4, byteorder='big')
            data = bytearray([0xAA, 0x55, 0x06, 0x01]) + freq_bytes
            crc = self.crc_func(data)
            data.append(crc)
            
            if self.serial_thread.send_data(data):
                self.update_log(f"设置频率: {freq/1000000:.6f} MHz")
        except ValueError:
            error_msg = f"频率格式错误: {freq_text}"
            self.update_log(error_msg)
            QMessageBox.critical(self, "格式错误", error_msg)
    
    def toggle_carrier(self):
        self.carrier_status = not self.carrier_status
        
        # 构建命令包
        # 命令格式: AA55 + 长度 + 命令类型(02表示载波测试) + 状态(01开启/00关闭) + 校验
        status_byte = 0x01 if self.carrier_status else 0x00
        data = bytearray([0xAA, 0x55, 0x03, 0x02, status_byte])
        crc = self.crc_func(data)
        data.append(crc)
        
        if self.serial_thread.send_data(data):
            if self.carrier_status:
                self.carrier_btn.setText("关闭载波测试")
                self.update_log("开启载波测试")
            else:
                self.carrier_btn.setText("开启载波测试")
                self.update_log("关闭载波测试")

    def update_connection_status(self, connected):
        if connected:
            self.connect_btn.setText("断开")
            self.set_freq_btn.setEnabled(True)
            self.set_power_btn.setEnabled(True)  # 启用功率设置按钮
            self.carrier_btn.setEnabled(True)
        else:
            self.connect_btn.setText("连接")
            self.set_freq_btn.setEnabled(False)
            self.set_power_btn.setEnabled(False)  # 禁用功率设置按钮
            self.carrier_btn.setEnabled(False)
            self.carrier_status = False
            self.carrier_btn.setText("开启载波测试")
    
    def toggle_connection(self):
        if self.connect_btn.text() == "连接":
            if self.port_combo.currentText():
                port = self.port_combo.currentText()
                baud = int(self.baud_combo.currentText())
                if self.serial_thread.connect_serial(port, baud):
                    self.serial_thread.start()
                    self.connect_btn.setText("断开")
                    self.set_freq_btn.setEnabled(True)
                    self.set_power_btn.setEnabled(True)  # 启用功率设置按钮
                    self.carrier_btn.setEnabled(True)
            else:
                self.update_log("请选择串口")
                QMessageBox.warning(self, "连接错误", "请先选择一个串口")
        else:
            self.serial_thread.disconnect_serial()
            self.connect_btn.setText("连接")
            self.set_freq_btn.setEnabled(False)
            self.set_power_btn.setEnabled(False)  # 禁用功率设置按钮
            self.carrier_btn.setEnabled(False)
            self.carrier_status = False
            self.carrier_btn.setText("开启载波测试")

    def update_log(self, message):
        current_time = time.strftime("%H:%M:%S", time.localtime())
        self.log_text.append(f"[{current_time}] {message}")
        # 滚动到底部
        self.log_text.verticalScrollBar().setValue(self.log_text.verticalScrollBar().maximum())
    
    def clear_log(self):
        self.log_text.clear()
    
    def closeEvent(self, event):
        # 关闭窗口时断开串口连接
        self.serial_thread.disconnect_serial()
        self.serial_thread.wait()
        event.accept()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = LoraControlApp()
    window.show()
    sys.exit(app.exec())